/*
 * %W% %E%
 * 
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.protocol;

import java.util.Iterator;
import java.util.HashMap;

import javax.rmi.CORBA.Tie;

import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.Context;
import org.omg.CORBA.ContextList;
import org.omg.CORBA.ExceptionList;
import org.omg.CORBA.NamedValue;
import org.omg.CORBA.NVList;
import org.omg.CORBA.Request;
import org.omg.CORBA.TypeCode;

import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.Delegate;
import org.omg.CORBA.portable.InputStream;
import org.omg.CORBA.portable.OutputStream;
import org.omg.CORBA.portable.RemarshalException;
import org.omg.CORBA.portable.ServantObject;

import com.sun.corba.se.pept.broker.Broker;
import com.sun.corba.se.pept.encoding.InputObject;
import com.sun.corba.se.pept.encoding.OutputObject;
import com.sun.corba.se.pept.protocol.ClientInvocationInfo;
import com.sun.corba.se.pept.protocol.ClientRequestDispatcher;
import com.sun.corba.se.pept.transport.ContactInfo;
import com.sun.corba.se.pept.transport.ContactInfoList;
import com.sun.corba.se.pept.transport.ContactInfoListIterator;

import com.sun.corba.se.spi.presentation.rmi.StubAdapter;
import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.protocol.CorbaClientDelegate ;
import com.sun.corba.se.spi.transport.CorbaContactInfo;
import com.sun.corba.se.spi.transport.CorbaContactInfoList;
import com.sun.corba.se.spi.transport.CorbaContactInfoListIterator;

import com.sun.corba.se.impl.corba.RequestImpl;
import com.sun.corba.se.impl.protocol.CorbaInvocationInfo;
import com.sun.corba.se.impl.transport.CorbaContactInfoListImpl;
import com.sun.corba.se.impl.util.JDKBridge;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;

// implements com.sun.corba.se.impl.core.ClientRequestDispatcher
// so RMI-IIOP Util.isLocal can call ClientRequestDispatcher.useLocalInvocation.

/**
 * @author Harold Carr
 */
public class CorbaClientDelegateImpl extends CorbaClientDelegate 
{
    private ORB orb;
    private ORBUtilSystemException wrapper ;

    private CorbaContactInfoList contactInfoList;

    public CorbaClientDelegateImpl(ORB orb, 
				   CorbaContactInfoList contactInfoList)
    {
	this.orb = orb;
	this.wrapper = ORBUtilSystemException.get( orb,
	    CORBALogDomains.RPC_PROTOCOL ) ;
	this.contactInfoList = contactInfoList;
    }
    
    //
    // framework.subcontract.Delegate
    //

    public Broker getBroker()
    {
	return orb;
    }

    public ContactInfoList getContactInfoList()
    {
	return contactInfoList;
    }

    //
    // CORBA_2_3.portable.Delegate
    //
    
    public OutputStream request(org.omg.CORBA.Object self, 
				String operation, 
				boolean responseExpected) 
    {
	ClientInvocationInfo invocationInfo = 
	    orb.createOrIncrementInvocationInfo();
	Iterator contactInfoListIterator = 
	    invocationInfo.getContactInfoListIterator();
	if (contactInfoListIterator == null) {
	    contactInfoListIterator = contactInfoList.iterator();
	    invocationInfo.setContactInfoListIterator(contactInfoListIterator);
	}
	if (! contactInfoListIterator.hasNext()) {
	    throw ((CorbaContactInfoListIterator)contactInfoListIterator)
		.getFailureException();
	}
	CorbaContactInfo contactInfo = (CorbaContactInfo) contactInfoListIterator.next();
	ClientRequestDispatcher subcontract = contactInfo.getClientRequestDispatcher();
	// Remember chosen subcontract for invoke and releaseReply.
	// NOTE: This is necessary since a stream is not available in
	// releaseReply if there is a client marshaling error or an
	// error in _invoke.
	invocationInfo.setClientRequestDispatcher(subcontract);
	return (OutputStream)
	    subcontract.beginRequest(self, operation,
				     !responseExpected, contactInfo);
    }
    
    public InputStream invoke(org.omg.CORBA.Object self, OutputStream output)
	throws
	    ApplicationException,
	    RemarshalException 
    {
	ClientRequestDispatcher subcontract = getClientRequestDispatcher();
	return (InputStream)
	    subcontract.marshalingComplete((Object)self, (OutputObject)output);
    }
    
    public void releaseReply(org.omg.CORBA.Object self, InputStream input) 
    {
	// NOTE: InputStream may be null (e.g., exception request from PI).
	ClientRequestDispatcher subcontract = getClientRequestDispatcher();
        subcontract.endRequest(orb, self, (InputObject)input);
	orb.releaseOrDecrementInvocationInfo();
    }

    private ClientRequestDispatcher getClientRequestDispatcher()
    {
        return (ClientRequestDispatcher)
            ((CorbaInvocationInfo)orb.getInvocationInfo())
	    .getClientRequestDispatcher();
    }

    public org.omg.CORBA.Object get_interface_def(org.omg.CORBA.Object obj) 
    {
	InputStream is = null;
	// instantiate the stub
	org.omg.CORBA.Object stub = null ;

        try {
	    OutputStream os = request(null, "_interface", true);
	    is = (InputStream) invoke((org.omg.CORBA.Object)null, os);

	    org.omg.CORBA.Object objimpl = 
		(org.omg.CORBA.Object) is.read_Object();

	    // check if returned object is of correct type
	    if ( !objimpl._is_a("IDL:omg.org/CORBA/InterfaceDef:1.0") )
		throw wrapper.wrongInterfaceDef(CompletionStatus.COMPLETED_MAYBE);

	    try {
                stub = (org.omg.CORBA.Object)
                    JDKBridge.loadClass("org.omg.CORBA._InterfaceDefStub").
		        newInstance();
	    } catch (Exception ex) {
		throw wrapper.noInterfaceDefStub( ex ) ;
	    }

	    org.omg.CORBA.portable.Delegate del = 
		StubAdapter.getDelegate( objimpl ) ;
	    StubAdapter.setDelegate( stub, del ) ;
	} catch (ApplicationException e) {
	    // This cannot happen.
	    throw wrapper.applicationExceptionInSpecialMethod( e ) ;
	} catch (RemarshalException e) {
	    return get_interface_def(obj);
	} finally {
	    releaseReply((org.omg.CORBA.Object)null, (InputStream)is);
        }

	return stub;
    }

    public boolean is_a(org.omg.CORBA.Object obj, String dest) 
    {
        // dest is the typeId of the interface to compare against.
        // repositoryIds is the list of typeIds that the stub knows about.

        // First we look for an answer using local information.

        String [] repositoryIds = StubAdapter.getTypeIds( obj ) ;
        String myid = contactInfoList.getTargetIOR().getTypeId();
        if ( dest.equals(myid) ) {
            return true;
	}
        for ( int i=0; i<repositoryIds.length; i++ ) {
            if ( dest.equals(repositoryIds[i]) ) {
		return true;
	    }
	}

        // But repositoryIds may not be complete, so it may be necessary to
        // go to server.

	InputStream is = null;
        try {
            OutputStream os = request(null, "_is_a", true);
            os.write_string(dest);
            is = (InputStream) invoke((org.omg.CORBA.Object) null, os);

	    return is.read_boolean();

        } catch (ApplicationException e) {
	    // This cannot happen.
	    throw wrapper.applicationExceptionInSpecialMethod( e ) ;
	} catch (RemarshalException e) {
	    return is_a(obj, dest);
	} finally {
	    releaseReply((org.omg.CORBA.Object)null, (InputStream)is);
        }
    }
    
    public boolean non_existent(org.omg.CORBA.Object obj) 
    {
	InputStream is = null;
        try {
            OutputStream os = request(null, "_non_existent", true);
            is = (InputStream) invoke((org.omg.CORBA.Object)null, os);

	    return is.read_boolean();

	} catch (ApplicationException e) {
	    // This cannot happen.
	    throw wrapper.applicationExceptionInSpecialMethod( e ) ;
	} catch (RemarshalException e) {
	    return non_existent(obj);
	} finally {
	    releaseReply((org.omg.CORBA.Object)null, (InputStream)is);
        }
    }
    
    public org.omg.CORBA.Object duplicate(org.omg.CORBA.Object obj) 
    {
	return obj;
    }
    
    public void release(org.omg.CORBA.Object obj) 
    {
	// DO NOT clear out internal variables to release memory
	// This delegate may be pointed-to by other objrefs.
    }

    // obj._get_delegate() == this due to the argument passing conventions in
    // portable.ObjectImpl, so we just ignore obj here.
    public boolean is_equivalent(org.omg.CORBA.Object obj,
				 org.omg.CORBA.Object ref)
    {
	if ( ref == null )
	    return false;

	// If ref is a local object, it is not a Stub!
	if (!StubAdapter.isStub(ref))
	    return false ;

	Delegate del = StubAdapter.getDelegate(ref) ;
	if (del == null)
	    return false ;

	// Optimize the x.is_equivalent( x ) case
	if (del == this)
	    return true;

	// If delegate was created by a different ORB, return false
	if (!(del instanceof CorbaClientDelegateImpl))
	    return false ;

	CorbaClientDelegateImpl corbaDelegate = (CorbaClientDelegateImpl)del ;
	CorbaContactInfoList ccil = 
	    (CorbaContactInfoList)corbaDelegate.getContactInfoList() ;
	return this.contactInfoList.getTargetIOR().isEquivalent( 
	    ccil.getTargetIOR() );
    }

    /**
     * This method overrides the org.omg.CORBA.portable.Delegate.equals method,
     * and does the equality check based on IOR equality.
     */
    public boolean equals(org.omg.CORBA.Object self, java.lang.Object other) 
    {
	if (other == null)
	    return false ;

        if (!StubAdapter.isStub(other)) {
            return false;   
        }
        
	Delegate delegate = StubAdapter.getDelegate( other ) ;
	if (delegate == null)
	    return false ;

        if (delegate instanceof CorbaClientDelegateImpl) {
            CorbaClientDelegateImpl otherDel = (CorbaClientDelegateImpl)
		delegate ;
            IOR otherIor = otherDel.contactInfoList.getTargetIOR();
            return this.contactInfoList.getTargetIOR().equals(otherIor);
        } 

	// Come here if other is not implemented by our ORB.
        return false;
    }

    public int hashCode(org.omg.CORBA.Object obj)
    {
	return this.hashCode() ;
    }

    public int hash(org.omg.CORBA.Object obj, int maximum) 
    {
	int h = this.hashCode();
	if ( h > maximum )
	    return 0;
	return h;
    }
    
    public Request request(org.omg.CORBA.Object obj, String operation) 
    {
	return new RequestImpl(orb, obj, null, operation, null, null, null,
			       null);
    }
    
    public Request create_request(org.omg.CORBA.Object obj,
				  Context ctx,
				  String operation,
				  NVList arg_list,
				  NamedValue result) 
    {
	return new RequestImpl(orb, obj, ctx, operation, arg_list,
			       result, null, null);
    }
    
    public Request create_request(org.omg.CORBA.Object obj,
				  Context ctx,
				  String operation,
				  NVList arg_list,
				  NamedValue result,
				  ExceptionList exclist, 
				  ContextList ctxlist) 
    {
	return new RequestImpl(orb, obj, ctx, operation, arg_list, result,
			       exclist, ctxlist);
    }
    
    public org.omg.CORBA.ORB orb(org.omg.CORBA.Object obj) 
    {
	return this.orb;
    }
    
    /**
     * Returns true if this object is implemented by a local servant.
     *
     * REVISIT: locatedIOR should be replaced with a method call that
     *	    returns the current IOR for this request (e.g. ContactInfoChooser).
     *
     * @param self The object reference which delegated to this delegate.
     * @return true only if the servant incarnating this object is located in
     * this ORB. 
     */
    public boolean is_local(org.omg.CORBA.Object self) 
    {
	// XXX this need to check isNextCallValid
        return contactInfoList.getEffectiveTargetIOR().getProfile().
	    isLocal();
    }
    
    public ServantObject servant_preinvoke(org.omg.CORBA.Object self,
					   String operation,
					   Class expectedType) 
    {
	return
	    contactInfoList.getLocalClientRequestDispatcher()
	    .servant_preinvoke(self, operation, expectedType);
    }
    
    public void servant_postinvoke(org.omg.CORBA.Object self,
				   ServantObject servant) 
    {
	contactInfoList.getLocalClientRequestDispatcher()
	    .servant_postinvoke(self, servant);
    }
    
    // XXX Should this be public?
    /* Returns the codebase for object reference provided.
     * @param self the object reference whose codebase needs to be returned.
     * @return the codebase as a space delimited list of url strings or
     * null if none.
     */
    public String get_codebase(org.omg.CORBA.Object self) 
    {
	if (contactInfoList.getTargetIOR() != null) {
	    return contactInfoList.getTargetIOR().getProfile().getCodebase();
	}
	return null;
    }

    public String toString(org.omg.CORBA.Object self) 
    {
	return contactInfoList.getTargetIOR().stringify();
    }
    
    ////////////////////////////////////////////////////
    //
    // java.lang.Object
    //

    public int hashCode()
    {
	return this.contactInfoList.hashCode();
    }
}

// End of file.

